今天要來仔細一點的介紹 Line Messaging API 的程式部分,昨天只講到讓 Linebot 重複使用者說的話,這一丁點用也沒有,所以今天來做一些好玩的,我們做出一個可以詢問天氣的對話機器人,問他天氣他會回應氣祥局的資訊。
因為我們要有氣象資料,但總不可能自己去設氣象站吧!?所以我們要存取中央氣象局的自動氣象站資訊,那該怎麼用呢?氣象局有開放資料 API,從那邊拿就好啦~ 但是這個服務需要註冊,所以先到 https://opendata.cwb.gov.tw/index 註冊一個帳號,不用擔心,這個服務不用錢呦~
訪問 API 你會看到一大串 JSON 格式的東西,這就是我們要的資料!
我們先建立一個 GetWeather(station)
函式,傳入值為氣象站的名稱,回傳值是該氣象站的天氣資訊,依照 API 的格式宣告一個合併 station 變數的字串變數 end_point,利用 requests 向 API 主機請求特定氣象站的天氣資訊,並透過 requests 內含的 json()
解析方法解析格式字串透過 for 迴圈依次比對,若有符合使用者想要的氣象站資訊則回傳,若無則回應 "not found"
。
def GetWeather(station):
end_point = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0001-001?Authorization=rdec-key-123-45678-011121314"
data = requests.get(end_point).json()
data = data["records"]["location"]
target_station = "not found"
for item in data:
if item["locationName"] == str(station):
target_station = item
return target_station
透過 GetWeather() 取得特定地點的天氣資訊後,我們必須把他轉成使用者容易看的資訊,如果直接回應 JSON 字串我想大概會被檢舉(? 所以我們先呼叫 GetWeather(),再檢查資料是否為"not found"
,若是,則代表使用者輸入不正確的測站,所以函式回應 False。
若成功取讀資料了,我們一層一層的取出我們想要的數據,第二段程式碼是在取得溫度及濕度資料,並且以大家比較容易看懂的方式製作訊息,最後呼叫 MakeAQI(station)
,呼叫 AQI 資料 API 並格式化,這個函式等等會說明,最後回傳訊息字串。
def MakeWeather(station):
WeatherData = GetWeather(station)
if WeatherData == "not found":
return False
WeatherData = WeatherData["weatherElement"]
msg = "豆芽天氣報告 - " + station
msg += "\n\n氣溫 = " + WeatherData[3]["elementValue"] + "℃\n"
msg += "濕度 = " + \
str(float(WeatherData[4]["elementValue"]) * 100) + "% RH\n"
msg += MakeAQI(station)
return msg
這邊我用了環保署的 API,取得特定測站的空氣品質資料,想必現在很多人都很在乎每天的天氣品質吧,這裡的做法有點像是 GetWeather() 和 MakeWeather() 的合體,透過 requests 取得 AQI 資料並檢查 HTTP code,若API伺服器回應 500 Server Error
則回應給使用者無 AQI 資料,畢竟使用者大多不懂 HTTP code,若成功則開始解析 JSON,並生成訊息格式,最後回傳訊息字串。
def MakeAQI(station):
end_point = "http://opendata.epa.gov.tw/webapi/api/rest/datastore/355000000I-000259?filters=SiteName eq '" + \
station + "'&sort=SiteName&offset=0&limit=1000"
data = requests.get(end_point)
AQImsg = ""
if data.status_code == 500:
return "無 AQI 資料"
else:
AQIdata = data.json()["result"]["records"][0]
AQImsg += "AQI = " + AQIdata["AQI"] + "\n"
AQImsg += "PM2.5 = " + AQIdata["PM2.5"] + " μg/m3\n"
AQImsg += "PM10 = " + AQIdata["PM10"] + " μg/m3\n"
AQImsg += "空品:" + AQIdata["Status"]
return AQImsg
最後整合進主程式,用 event.message.text.split(" ")
以空格分開使用者傳送的訊息,很粗略的用 if 判斷式解析語句,其他就和昨天的程式碼相似,程式碼我也不私藏啦~如果看不懂的話複製貼上也是可以,記得 Token 和 Secret 要改成你自己的呦!
import os
from flask import Flask, request, abort
import requests
import json
#LINE bot 必要套件
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,ImageMessage,ImageSendMessage
)
app = Flask(__name__)
# Channel Access Token
line_bot_api = LineBotApi("YOUR_TOKEN")
# Channel Secret
handler = WebhookHandler("YOUR_Secret")
def MakeAQI(station):
end_point = "http://opendata.epa.gov.tw/webapi/api/rest/datastore/355000000I-000259?filters=SiteName eq '" + \
station + "'&sort=SiteName&offset=0&limit=1000"
data = requests.get(end_point)
AQImsg = ""
if data.status_code == 500:
return "無 AQI 資料"
else:
AQIdata = data.json()["result"]["records"][0]
AQImsg += "AQI = " + AQIdata["AQI"] + "\n"
AQImsg += "PM2.5 = " + AQIdata["PM2.5"] + " μg/m3\n"
AQImsg += "PM10 = " + AQIdata["PM10"] + " μg/m3\n"
AQImsg += "空品:" + AQIdata["Status"]
return AQImsg
def GetWeather(station):
end_point = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0001-001?Authorization=rdec-key-123-45678-011121314"
data = requests.get(end_point).json()
data = data["records"]["location"]
target_station = "not found"
for item in data:
if item["locationName"] == str(station):
target_station = item
return target_station
def MakeWeather(station):
WeatherData = GetWeather(station)
if WeatherData == "not found":
return False
WeatherData = WeatherData["weatherElement"]
msg = "豆芽天氣報告 - " + station
msg += "\n\n氣溫 = " + WeatherData[3]["elementValue"] + "℃\n"
msg += "濕度 = " + \
str(float(WeatherData[4]["elementValue"]) * 100) + "% RH\n"
msg += MakeAQI(station)
return msg
def MakeRailFall(station):
result = requests.get(
"https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0002-001?Authorization=rdec-key-123-45678-011121314")
msg = "豆芽降雨報告 - " + station + "\n\n"
if(result.status_code != 200):
return "雨量資料讀取失敗"
else:
railFallData = result.json()
for item in railFallData["records"]["location"]:
if station in item["locationName"]:
msg += "目前雨量:" + \
item["weatherElement"][7]["elementValue"] + "mm\n"
if item["weatherElement"][3]["elementValue"] == "-998.00":
msg += "三小時雨量:0.00mm\n"
else:
msg += "三小時雨量:" + \
item["weatherElement"][3]["elementValue"] + "mm\n"
msg += "日雨量:" + \
item["weatherElement"][6]["elementValue"] + "mm\n"
return msg
return "沒有這個測站啦"
@app.route("/callback", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
cmd = event.message.text.split(" ")
if cmd[0] == "天氣":
station = cmd[1]
WeatherMsg = MakeWeather(station)
if not WeatherMsg:
WeatherMsg = "沒這個氣象站啦"
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=WeatherMsg))
if cmd[0] == "雨量":
station = cmd[1]
RailFallMsg = MakeRailFall(station)
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=RailFallMsg))
if __name__ == "__main__":
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
寫好後依照昨天的方法,部屬到 heroku 上,如果忘記沒關係,我再打一次,先開啟 cmd,到專案目錄下輸入以下指令
git add .
git commit -am "Add weather feature."
git push heroku master
然後去試試看,發送天氣 鳳山給機器人,看看他有沒有成功回應啊~
我自己也有寫一個 Line 對話機器人,裡面也有天氣或雨量等等的功能,可以給大家參考參考
加好友 QR code:
呈現結果:
我覺得因為在台灣很多人用 Line,幾乎大家都有,所以透過 Linebot 來建立服務十分方便,使用者不須額外安裝程式,也可以達到應用程式的效果,加上對答的模式符合現在服務的潮流,這一部分很是值得研究研究,而且 Line 公司近日在開發者社群這一塊挺用心的,API/Documents 等等都準備得很周全,甚至有開發者聚會,資源相對來說是很足夠的!
參考資料
https://developers.line.biz/en/reference/messaging-api/